
//          Copyright Oliver Kowalke 2009.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <stdio.h>
#include <stdlib.h>

#include <vector>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <thread>

#include <boost/context/detail/config.hpp>
#include <boost/context/detail/fcontext.hpp>
#include <boost/assert.hpp>

namespace bctxf = boost::context::detail;

void tcontext_entry1(bctxf::transfer_t tf) 
{
	std::cout << __FUNCDNAME__ << std::endl;
	std::cout << "tcontext_entry1 1" << std::endl;

	bctxf::jump_fcontext(tf.fctx, 0); 
	
	std::cout << "tcontext_entry1 2" << std::endl;

	bctxf::jump_fcontext(tf.fctx, 0);
}



template< std::size_t Max, std::size_t Default, std::size_t Min >
class simple_stack_allocator
{
public:
	static std::size_t maximum_stacksize()
	{
		return Max;
	}

	static std::size_t default_stacksize()
	{
		return Default;
	}

	static std::size_t minimum_stacksize()
	{
		return Min;
	}

	void * allocate(std::size_t size) const
	{
		BOOST_ASSERT(minimum_stacksize() <= size);
		BOOST_ASSERT(maximum_stacksize() >= size);

		void * limit = malloc(size);
		if (!limit) throw std::bad_alloc();

		return static_cast<char *>(limit) + size;
	}

	void deallocate(void * vp, std::size_t size) const
	{

		void * limit = static_cast<char *>(vp) - size;
		free(limit);
	}
};

typedef simple_stack_allocator<
	8 * 1024 * 1024, 64 * 1024, 8 * 1024
>                                       stack_allocator;

namespace ctx = boost::context::detail;

typedef simple_stack_allocator<
	8 * 1024 * 1024, // 8MB
	64 * 1024, // 64kB
	8 * 1024 // 8kB
>       stack_allocator;

void Test_Jump1()
{
	std::vector<uint8_t> stack;
	stack.resize(1024 * 10);
	std::cout << "make_fcontext" << std::endl;
	// ����ַ������stackĩβ
	bctxf::fcontext_t ctx = bctxf::make_fcontext(stack.data() + stack.size(), stack.size(), &tcontext_entry1);

	std::cout << "2" << std::endl;
	bctxf::transfer_t tx  = bctxf::jump_fcontext(ctx, 0);
	std::cout << "3" << std::endl;
	tx = bctxf::jump_fcontext(tx.fctx, 0);
	std::cout << "3a" << std::endl;
}

/**
 * @brief: ʵ�ʲ��Թ����ǲ�֧�ֶ��߳���context
 */
void Test_JumpWithThread()
{
	std::vector<uint8_t> stack;
	stack.resize(1024 * 10);
	std::cout << "make_fcontext" << std::endl;
	// ����ַ������stackĩβ
	bctxf::fcontext_t ctx = bctxf::make_fcontext(stack.data() + stack.size(), stack.size(), &tcontext_entry1);

	bctxf::transfer_t tx;
	memset(&tx, 0, sizeof(tx));

	std::cout << "2" << std::endl;
	std::thread t1([&]()->void {
		tx = bctxf::jump_fcontext(ctx, &tx);
	});
	
	t1.join();

	std::cout << "3" << std::endl;
	tx = bctxf::jump_fcontext(tx.fctx, 0);
	std::cout << "3a" << std::endl;

	//std::cout << "3" << std::endl;
	//std::thread t2([&]()->void {
	//	tx = bctxf::jump_fcontext(tx.fctx, &tx);
	//});

	//t2.join();

	std::cout << "3a" << std::endl;
}

int value1 = 0;

void f1(ctx::transfer_t t) {
	++value1;
	std::cout << "make_fcontext" << std::endl;
	ctx::jump_fcontext(t.fctx, t.data);
}


void Test_Jump()
{
	value1 = 0;
	stack_allocator alloc;
	void * sp = alloc.allocate(stack_allocator::default_stacksize());
	ctx::fcontext_t ctx = ctx::make_fcontext(sp, stack_allocator::default_stacksize(), f1);


	ctx::jump_fcontext(ctx, 0);
	alloc.deallocate(sp, stack_allocator::default_stacksize());
}

int main()
{
	std::cout << "in main" << std::endl;
	// ��Ϊÿ���л�context��context��ַ������
	// ��������ٴη�װ��ǿ�ư�ID����ͨ����ô͸��һ�£�ÿ�ε��Ⱥ����id��Ӧctx����
	// ����һ�����⣬Э��ʲôʱ����Ҫ������
	//std::vector<uint8_t> stack;
	//stack.resize(1024*100);
	//uint8_t n = stack.at(1025);
	Test_JumpWithThread();
	//Test_Jump();
	
	std::cout << "out main" << std::endl;

	return 0;
}